package org.lc.scoretj.util;

import org.lc.scoretj.model.Score;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

/**
 * 成绩计算工具类
 */
public class GradeCalculator {

    /**
     * 成绩等级枚举
     */
    public enum GradeLevel {
    EXCELLENT("优秀", new BigDecimal("90.000"), new BigDecimal("100.000")),
    GOOD("良好", new BigDecimal("80.000"), new BigDecimal("90.000")),
    PASS("及格", new BigDecimal("60.000"), new BigDecimal("80.000")),
    FAIL("不及格", new BigDecimal("0.000"), new BigDecimal("60.000"));

        private final String name;
        private final BigDecimal minScore; // inclusive
        private final BigDecimal maxScore; // exclusive except for EXCELLENT which includes max

        GradeLevel(String name, BigDecimal minScore, BigDecimal maxScore) {
            this.name = name;
            this.minScore = minScore;
            this.maxScore = maxScore;
        }

        public String getName() {
            return name;
        }

        public BigDecimal getMinScore() {
            return minScore;
        }

        public BigDecimal getMaxScore() {
            return maxScore;
        }

        public static GradeLevel getGradeLevel(BigDecimal score) {
            if (score == null) {
                return FAIL;
            }

            // compare with scale 3
            BigDecimal s = score.setScale(3, RoundingMode.HALF_UP);
            for (GradeLevel level : values()) {
                if (level == EXCELLENT) {
                    // EXCELLENT: [min, max] inclusive upper bound
                    if (s.compareTo(level.getMinScore()) >= 0 && s.compareTo(level.getMaxScore()) <= 0) {
                        return level;
                    }
                } else {
                    // other levels: [min, max)
                    if (s.compareTo(level.getMinScore()) >= 0 && s.compareTo(level.getMaxScore()) < 0) {
                        return level;
                    }
                }
            }
            return FAIL;
        }

        public static GradeLevel getGradeLevel(Float score) {
            if (score == null) return FAIL;
            return getGradeLevel(BigDecimal.valueOf(score).setScale(3, RoundingMode.HALF_UP));
        }
    }

    /**
     * 计算平均分
     * @param scores 成绩列表
     * @param scoreGetter 成绩获取函数
     * @return 平均分
     */
    public static Float calculateAverage(List<Score> scores, ScoreGetter scoreGetter) {
        if (scores == null || scores.isEmpty()) {
            return null;
        }
        BigDecimal sum = BigDecimal.ZERO;
        int count = 0;
        for (Score score : scores) {
            Float value = scoreGetter.getScore(score);
            if (value != null) {
                sum = sum.add(BigDecimal.valueOf(value));
                count++;
            }
        }

        if (count == 0) return null;
        BigDecimal avg = sum.divide(BigDecimal.valueOf(count), 3, RoundingMode.HALF_UP);
        return avg.floatValue();
    }

    /**
     * 计算标准差
     * @param scores 成绩列表
     * @param scoreGetter 成绩获取函数
     * @return 标准差
     */
    public static Float calculateStandardDeviation(List<Score> scores, ScoreGetter scoreGetter) {
        if (scores == null || scores.isEmpty()) {
            return null;
        }

        Float average = calculateAverage(scores, scoreGetter);
        if (average == null) {
            return null;
        }
        double sumOfSquares = 0.0;
        int count = 0;
        for (Score score : scores) {
            Float value = scoreGetter.getScore(score);
            if (value != null) {
                // use rounded value at 3 decimals for each comparison
                BigDecimal v = BigDecimal.valueOf(value).setScale(3, RoundingMode.HALF_UP);
                BigDecimal a = BigDecimal.valueOf(average).setScale(3, RoundingMode.HALF_UP);
                double diff = v.subtract(a).doubleValue();
                sumOfSquares += diff * diff;
                count++;
            }
        }

        if (count == 0) return null;
        double sd = Math.sqrt(sumOfSquares / count);
        BigDecimal sdRounded = BigDecimal.valueOf(sd).setScale(3, RoundingMode.HALF_UP);
        return sdRounded.floatValue();
    }

    /**
     * 计算中位数
     */
    public static Float calculateMedian(List<Score> scores, ScoreGetter scoreGetter) {
        if (scores == null || scores.isEmpty()) {
            return null;
        }
        List<BigDecimal> vals = new java.util.ArrayList<>();
        for (Score score : scores) {
            Float value = scoreGetter.getScore(score);
            if (value != null) vals.add(BigDecimal.valueOf(value).setScale(3, RoundingMode.HALF_UP));
        }

        if (vals.isEmpty()) return null;

        java.util.Collections.sort(vals);
        int n = vals.size();
        BigDecimal median;
        if (n % 2 == 1) {
            median = vals.get(n / 2);
        } else {
            BigDecimal a = vals.get(n / 2 - 1);
            BigDecimal b = vals.get(n / 2);
            median = a.add(b).divide(BigDecimal.valueOf(2), 3, RoundingMode.HALF_UP);
        }
        return median.floatValue();
    }

    /**
     * 统计各等级人数
     * @param scores 成绩列表
     * @param scoreGetter 成绩获取函数
     * @return 各等级人数统计
     */
    public static Map<String, Integer> countGradeLevels(List<Score> scores, ScoreGetter scoreGetter) {
    Map<String, Integer> countMap = new HashMap<>();
    countMap.put("excellent", 0);
    countMap.put("good", 0);
    countMap.put("pass", 0);
    countMap.put("fail", 0);

        if (scores == null || scores.isEmpty()) {
            return countMap;
        }

        for (Score score : scores) {
            Float value = scoreGetter.getScore(score);
            if (value != null) {
                // round to 3 decimals before grading
                BigDecimal v = BigDecimal.valueOf(value).setScale(3, RoundingMode.HALF_UP);
                GradeLevel level = GradeLevel.getGradeLevel(v);
                switch (level) {
                    case EXCELLENT:
                        countMap.put("excellent", countMap.get("excellent") + 1);
                        break;
                    case GOOD:
                        countMap.put("good", countMap.get("good") + 1);
                        break;
                    case PASS:
                        countMap.put("pass", countMap.get("pass") + 1);
                        break;
                    case FAIL:
                        countMap.put("fail", countMap.get("fail") + 1);
                        break;
                }
            }
        }

        return countMap;
    }

    /**
     * 成绩获取函数接口
     */
    @FunctionalInterface
    public interface ScoreGetter {
        Float getScore(Score score);
    }

    /**
     * 形成性成绩获取器
     */
    public static final ScoreGetter XCX_SCORE_GETTER = score -> score.getXcxScore();

    /**
     * 终结性成绩获取器
     */
    public static final ScoreGetter ZJX_SCORE_GETTER = score -> score.getZjxScore();

    /**
     * 总评成绩获取器
     */
    public static final ScoreGetter ZP_SCORE_GETTER = score -> score.getZpScore();
}